﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using RobotEscape.Screens;
using RobotEscape.Entities;
using System.Windows.Media.Imaging;
using System.Collections.Generic;

namespace RobotEscape
{
    public class Level
    {
        public int LevelNumber = 1;
        private int[,] mask;
        private Tile[,] tiles;
        public int PosY = 20;
        public int width, height, tileCountX, tileCountY;
        Rectangle tilesRect = new Rectangle();
        ImageBrush brush;
        public GameScreen GameScreen;
        public double GRAVITY = 0.10;
        public double TERMINAL_VELOCITY = 3;
        
        public List<Plate> Plates = new List<Plate>();

        public Level(GameScreen gameScreen, int level)
        {
            GameScreen = gameScreen;
            LevelNumber = level;
            LoadLevel();

            Canvas.SetZIndex(tilesRect, -1);
            Game.Instance.Canvas.Children.Add(tilesRect);
        }

        private void LoadLevel()
        {
            BuildTileMap();
            BuildTileMask();
            BuildWB();

            Canvas.SetLeft(this.tilesRect, 0);
            Canvas.SetTop(this.tilesRect, PosY);
            this.tilesRect.Width = this.width;
            this.tilesRect.Height = this.height;

        }

        private void BuildTileMap()
        {
            string levelBlueprint = getLevelBlueprint();
            string[] bp = levelBlueprint.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            tileCountX = bp[0].Length;
            tileCountY = bp.Length;
            width = tileCountX * Tile.Size;
            height = tileCountY * Tile.Size;
            tiles = new Tile[tileCountX, tileCountY];


            for (int x = 0; x < tileCountX; x++)
            {
                for (int y = 0; y < tileCountY; y++)
                {
                    char tileType = bp[y][x];
                    tiles[x, y] = loadTile(tileType, x, y, x * Tile.Size, y * Tile.Size + this.PosY);
                }
            }
        }

        private void BuildTileMask()
        {
            string levelBlueprint = getLevelMaskBlueprint();
            string[] bp = levelBlueprint.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            mask = new int[tileCountX, tileCountY];

            for (int x = 0; x < tileCountX; x++)
            {
                for (int y = 0; y < tileCountY; y++)
                {
                    char tileType = bp[y][x];
                    int i;
                    if (int.TryParse(tileType.ToString(), out i))
                        mask[x, y] = i;
                }
            }
        }
        private void BuildWB()
        {
            WriteableBitmap wbTiles = new WriteableBitmap(this.width, this.height);

            WriteableBitmap wbTileMaster = new WriteableBitmap(ResourceHelper.GetBitmap("Assets/images/walls.png"));

            for (int x = 0; x < tileCountX; x++)
            {
                for (int y = 0; y < tileCountY; y++)
                {
                    if (mask[x, y] == 0)
                    {
                        Tile tile = tiles[x, y];
                        wbTiles.Blit(new Rect(x * Tile.Size, y * Tile.Size, Tile.Size, Tile.Size), wbTileMaster
                                , new Rect(tile.ImageSource.X, tile.ImageSource.Y, Tile.Size, Tile.Size));
                    }
                    else
                    {
                        wbTiles.Blit(new Rect(x * Tile.Size, y * Tile.Size, Tile.Size, Tile.Size), wbTileMaster
                                , new Rect(160, 0, Tile.Size, Tile.Size));
                    }
                }
            }
            brush = new ImageBrush
            {
                Stretch = Stretch.None,
                AlignmentX = AlignmentX.Left,
                AlignmentY = AlignmentY.Top
            };

            brush.ImageSource = wbTiles;
            TranslateTransform transform = new TranslateTransform();
            brush.Transform = transform;

            this.tilesRect.Fill = brush;
        }

        public void UnmaskSection(int section)
        {
            for (int x = 0; x < tileCountX; x++)
            {
                for (int y = 0; y < tileCountY; y++)
                {
                    if (mask[x, y] == section)
                    {
                        mask[x, y] = 0;
                    }
                }
            }
            BuildWB();
        }

        private string getLevelBlueprint()
        {
            return ResourceHelper.GetString("Assets/levels/" + LevelNumber + ".txt");
        }

        private string getLevelMaskBlueprint()
        {
            return ResourceHelper.GetString("Assets/levels/" + LevelNumber + "_mask.txt");
        }

        private Tile loadTile(char tileType, int x, int y, int pixelX, int pixelY)
        {
            switch (tileType)
            {
                case '#':
                    // solid wall
                    if (Game.Instance.rand.Next(0,8) == 0)
                        return new Tile(new Point(20, 20), TileCollision.Impassable);
                    else
                        return new Tile(new Point(20, 0), TileCollision.Impassable);
                case '1':
                    // robot
                    Player player = new Player(this);
                    player.PosX = pixelX;
                    player.PosY = pixelY;
                    player.Activated = true;
                    player.PlayerNum = 1;
                    GameScreen.AddEntity(player);
                    return new Tile(new Point(0, 0), TileCollision.Passable);
                case '2':
                case '3':
                case '4':
                    // dead robot
                    Player player2 = new Player(this, true);
                    player2.PosX = pixelX;
                    player2.PosY = pixelY;
                    player2.PlayerNum = int.Parse(tileType.ToString());
                    GameScreen.AddEntity(player2);
                    return new Tile(new Point(0, 0), TileCollision.Passable);
                case 'R':
                    Plate plateR = new Plate(PlateColor.Red, this);
                    plateR.PosX = pixelX;
                    plateR.PosY = pixelY;
                    Plates.Add(plateR);
                    GameScreen.AddEntity(plateR);
                    return new Tile(new Point(40, 0), TileCollision.Passable);
                case 'G':
                    Plate plateG = new Plate(PlateColor.Green, this);
                    plateG.PosX = pixelX;
                    plateG.PosY = pixelY;
                    Plates.Add(plateG);
                    GameScreen.AddEntity(plateG);
                    return new Tile(new Point(60, 0), TileCollision.Passable);
                case 'B':
                    Plate plateB = new Plate(PlateColor.Blue, this);
                    plateB.PosX = pixelX;
                    plateB.PosY = pixelY;
                    Plates.Add(plateB);
                    GameScreen.AddEntity(plateB);
                    return new Tile(new Point(80, 0), TileCollision.Passable);
                case 'Y':
                    Plate plateY = new Plate(PlateColor.Yellow, this);
                    plateY.PosX = pixelX;
                    plateY.PosY = pixelY;
                    Plates.Add(plateY);
                    GameScreen.AddEntity(plateY);
                    return new Tile(new Point(100, 0), TileCollision.Passable);
                case 'X':
                    Exit exit = new Exit();
                    exit.PosX = pixelX;
                    exit.PosY = pixelY;
                    GameScreen.AddEntity(exit);
                    return new Tile(new Point(0, 0), TileCollision.Passable);
                case '>':
                    // spike
                    Spike a = new Spike(SpikeWall.Left);
                    a.PosX = pixelX;
                    a.PosY = pixelY;
                    GameScreen.AddEntity(a);
                    if (Game.Instance.rand.Next(0, 8) == 0)
                        return new Tile(new Point(20, 20), TileCollision.Impassable);
                    else
                        return new Tile(new Point(20, 0), TileCollision.Impassable);
                case 'V':
                    // spike
                    Spike s = new Spike(SpikeWall.Top);
                    s.PosX = pixelX;
                    s.PosY = pixelY;
                    GameScreen.AddEntity(s);
                    if (Game.Instance.rand.Next(0, 8) == 0)
                        return new Tile(new Point(20, 20), TileCollision.Impassable);
                    else
                        return new Tile(new Point(20, 0), TileCollision.Impassable);
                case '<':
                    // spike
                    Spike d = new Spike(SpikeWall.Right);
                    d.PosX = pixelX;
                    d.PosY = pixelY;
                    GameScreen.AddEntity(d);
                    if (Game.Instance.rand.Next(0, 8) == 0)
                        return new Tile(new Point(20, 20), TileCollision.Impassable);
                    else
                        return new Tile(new Point(20, 0), TileCollision.Impassable);
                case '^':
                    // spike
                    Spike f = new Spike(SpikeWall.Bottom);
                    f.PosX = pixelX;
                    f.PosY = pixelY;
                    GameScreen.AddEntity(f);
                    if (Game.Instance.rand.Next(0, 8) == 0)
                        return new Tile(new Point(20, 20), TileCollision.Impassable);
                    else
                        return new Tile(new Point(20, 0), TileCollision.Impassable);

                case 'C':
                    // corruptor
                    Corruptor c = new Corruptor();
                    c.PosX = pixelX;
                    c.PosY = pixelY;
                    GameScreen.AddEntity(c);
                    return new Tile(new Point(120, 0), TileCollision.Passable);

                case 'L':
                    // lava
                    Hazzard h = new Hazzard();
                    h.PosX = pixelX;
                    h.PosY = pixelY;
                    GameScreen.AddEntity(h);
                    return new Tile(new Point(180, 0), TileCollision.Passable);
                default:
                    if (Game.Instance.rand.Next(0,8) == 0)
                        return new Tile(new Point(0, 20), TileCollision.Passable);
                    else
                        return new Tile(new Point(0, 0), TileCollision.Passable);
            }
        }

        public Point GetClosestTile(double pixelX, double pixelY)
        {
            int x = (int)pixelX / Tile.Size;
            int y = (int)pixelY / Tile.Size;
            return new Point(x, y);
        }

        public Tile GetTile(int x, int y)
        {
            return tiles[x, y - 1];
        }

        public TileCollision GetCollision(int x, int y)
        {
            if (x < 0 || x > tileCountX || y < 0 || y > tileCountY) return TileCollision.Impassable;

            return tiles[x, y-1].Collision; // -1 for the hud offset
        }

        /// <summary>
        /// Gets the bounding rectangle of a tile in world space.
        /// </summary>        
        public Rect GetBounds(int x, int y)
        {
            return new Rect(x * Tile.Size, y * Tile.Size, Tile.Size, Tile.Size);
        }

        public void Update(GameTime time)
        {
        }

        internal void Remove()
        {
            if (Game.Instance.Canvas.Children.Contains(tilesRect))
                Game.Instance.Canvas.Children.Remove(tilesRect);
        }
    }
}
